package com.yunwushanhe.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yunwushanhe.bean.req.UserLoginReq;
import com.yunwushanhe.bean.ret.ResponseResult;
import com.yunwushanhe.bean.vo.UserInfoVO;
import com.yunwushanhe.bean.vo.UserLoginVO;
import com.yunwushanhe.constant.Constant;
import com.yunwushanhe.constant.RedisConstant;
import com.yunwushanhe.entity.LoginUser;
import com.yunwushanhe.enums.AppHttpCodeEnum;
import com.yunwushanhe.exception.BlogSystemException;
import com.yunwushanhe.mapper.UserMapper;
import com.yunwushanhe.entity.User;
import com.yunwushanhe.service.UserService;
import com.yunwushanhe.util.BeanCopyUtil;
import com.yunwushanhe.util.JwtUtil;
import com.yunwushanhe.util.RedisCache;
import com.yunwushanhe.util.UserContextUtil;
import org.omg.CORBA.SystemException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.HashMap;

/**
 * 用户表(User)表服务实现类
 *
 * @author 抬头看看天
 * @since 2023-08-05 22:17:30
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private UserMapper userMapper;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private RedisCache redisCache;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Override /* 这里的代码视乎与 UsernamePasswordAuthenticationFilter 类中的 attemptAuthentication 方法中代码有些相似，但是这个是有自定义前端页面时才这么些*/
    public ResponseResult<UserLoginVO> login(UserLoginReq userLoginReq) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userLoginReq.getUserName(), userLoginReq.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken); // 会调用 UserDetailsServiceImpl 这个里面的方法
        //判断是否认证通过
        if(ObjectUtils.isEmpty(authenticate)){
            throw new RuntimeException("用户名或密码错误"); // 这个异常会交给全局异常处理器处理
        }
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String token = JwtUtil.createJWT(userId);
        //把信息存入redis中
        redisCache.setCacheObject(RedisConstant.LOGIN_USERINFO_KEY +":"+userId,loginUser);

        UserInfoVO userInfoVO = BeanCopyUtil.copyBean(loginUser.getUser(), UserInfoVO.class);
        UserLoginVO userLoginVO = new UserLoginVO(token, userInfoVO);
        return ResponseResult.ok(userLoginVO);
    }

    @Override
    public ResponseResult<Object> logout() {
        Long userId = UserContextUtil.getUserId();
        redisCache.deleteObject(RedisConstant.LOGIN_USERINFO_KEY+":"+userId.toString());
        return ResponseResult.ok();
    }

    @Override
    public ResponseResult<UserInfoVO> getUserInfo() {
        User user = getById(UserContextUtil.getUserId());
        UserInfoVO userInfoVO = BeanCopyUtil.copyBean(user, UserInfoVO.class);
        return ResponseResult.ok(userInfoVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult<Object> updateUserInfo(User user) {
        // 这里实现条件更新，可以防止接口路径被暴露后，被人拿来乱改数据
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id",user.getId());
        updateWrapper.set("nick_name",user.getNickName());
        updateWrapper.set("avatar",user.getAvatar());
        updateWrapper.set("sex",user.getSex());
        updateWrapper.set("email",user.getEmail());
        update(updateWrapper);
        return ResponseResult.ok();
    }

    @Override //这里体现出了Validation，要去学习一下
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult<Object> register(User user) {
        //对数据进行非空判断
        if(!StringUtils.hasText(user.getUserName())){
            throw new BlogSystemException(AppHttpCodeEnum.USERNAME_NOT_NULL);
        }
        if(!StringUtils.hasText(user.getPassword())){
            throw new BlogSystemException(AppHttpCodeEnum.PASSWORD_NOT_NULL);
        }
        if(!StringUtils.hasText(user.getEmail())){
            throw new BlogSystemException(AppHttpCodeEnum.EMAIL_NOT_NULL);
        }
        if(!StringUtils.hasText(user.getNickName())){
            throw new BlogSystemException(AppHttpCodeEnum.NICKNAME_NOT_NULL);
        }

        //对数据进行是否存在的判断
        if(userNameExist(user.getUserName())){
            throw new BlogSystemException(AppHttpCodeEnum.USERNAME_EXIST);
        }
        if(userEmailExist(user.getEmail())){
            throw new BlogSystemException(AppHttpCodeEnum.EMAIL_EXIST);
        }
        if(userPhoneNumberExist(user.getPhonenumber())){
            throw new BlogSystemException(AppHttpCodeEnum.PHONE_NUMBER_EXIST);
        }

        String encodePassword = passwordEncoder.encode(user.getPassword());
        user.setPassword(encodePassword);
        save(user);
        return ResponseResult.ok();
    }

    private boolean userPhoneNumberExist(String phoneNumber) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getPhonenumber,phoneNumber);
        return count(queryWrapper) > 0;
    }

    private boolean userEmailExist(String email) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getEmail,email);
        return count(queryWrapper) > 0;
    }

    private boolean userNameExist(String userName){
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName,userName);
        return count(queryWrapper) > 0;
    }

}

